package online.heycm.platform.web.common.encrypt;

import lombok.SneakyThrows;
import online.heycm.platform.common.entity.result.PageResult;
import online.heycm.platform.common.entity.result.Result;
import online.heycm.platform.web.common.annotation.DecryptField;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.CollectionUtils;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * ResponseBody 解密拦截
 *
 * @author hey
 * @version 1.0
 * @date 2024/1/16 16:07
 */
public abstract class AbstractDecryptResponseBodyAdvice implements ResponseBodyAdvice<Result<?>> {

    /**
     * 解密
     *
     * @param content 内容
     * @return
     */
    protected abstract String decrypt(String content);

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    @SneakyThrows
    @Override
    public Result<?> beforeBodyWrite(Result<?> body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (body == null || !body.ok() || body.getData() == null) {
            return body;
        }
        Object data = body.getData();
        if (data instanceof Boolean) {
            return body;
        }
        if (data instanceof Number) {
            return body;
        }
        if (data instanceof Collection) {
            Collection<?> collection = (Collection<?>) data;
            decryptCollection(collection);
            return body;
        }
        if (data instanceof PageResult) {
            PageResult<?> pageResult = (PageResult<?>) data;
            List<?> records = pageResult.getRecords();
            decryptCollection(records);
            return body;
        }
        decryptObject(data);
        return body;
    }

    /**
     * 解密
     *
     * @param collection 解密集合
     * @throws IllegalAccessException
     */
    private void decryptCollection(Collection<?> collection) throws IllegalAccessException {
        if (CollectionUtils.isEmpty(collection)) {
            return;
        }
        List<Field> decryptFields = null;
        int size = 0;
        for (Object element : collection) {
            if (size == 0) {
                decryptFields = getDecryptFields(element);
                size = -1;
            }
            if (decryptFields.isEmpty()) {
                break;
            }
            decryptObject(element, decryptFields);
        }
    }

    /**
     * 解密
     *
     * @param object 解密对象
     * @throws IllegalAccessException
     */
    private void decryptObject(Object object) throws IllegalAccessException {
        List<Field> decryptFields = getDecryptFields(object);
        decryptObject(object, decryptFields);
    }

    /**
     * 解密
     *
     * @param object        解密对象
     * @param decryptFields 需要解密的字段
     * @throws IllegalAccessException
     */
    private void decryptObject(Object object, List<Field> decryptFields) throws IllegalAccessException {
        if (decryptFields.isEmpty()) {
            return;
        }
        for (Field field : decryptFields) {
            field.setAccessible(true);
            Object originalValue = field.get(object);
            if (originalValue != null) {
                field.set(object, this.decrypt(originalValue.toString()));
            }
        }
    }

    /**
     * 获取需要解密的字段
     *
     * @param object 解密对象
     * @return
     */
    private List<Field> getDecryptFields(Object object) {
        List<Field> decryptFields = new ArrayList<>();
        for (Field field : object.getClass().getDeclaredFields()) {
            DecryptField annotation = field.getAnnotation(DecryptField.class);
            if (annotation != null) {
                decryptFields.add(field);
            }
        }
        return decryptFields;
    }
}
